#include <cassert>
#include <cstdlib>
#include <cstring>
#include "Value.h"

using namespace moses;

Value::Value(const Type t)
: m_type(t)
{
	switch (m_type.GetTypeID())
	{
		case Type::STRING:
			m_value.v_string = new std::string("");
			break;
		default:;
	}
}

// Boolean
Value::Value(bool value)
: m_type(Type::BOOL)
{
	m_value.v_boolean = value;
}

// Integer, represented by a float
Value::Value(int value)
: m_type(Type::INT)
{
	m_value.v_int = (float)value;
}

// Float
Value::Value(float value)
: m_type(Type::FLOAT)
{
	m_value.v_float = value;
}

// String
Value::Value(const char* value)
: m_type(Type::STRING)
{
	m_value.v_string = new std::string(value);
}

// String
Value::Value(const std::string& value)
: m_type(Type::STRING)
{
	m_value.v_string = new std::string(value);
}

Value::~Value()
{
	switch (m_type.GetTypeID())
	{
		case Type::STRING:
			delete m_value.v_string;
			break;
		default:;
	}
}

Value::Value(const Value &v)
:  m_type(Type::NONE)
{
	*this = v;
}

Value& Value::operator=(const Value &v)
{
	if (this != &v)
	{		
		if (m_type.GetTypeID() == Type::STRING)
			delete m_value.v_string;
	
		m_type = v.m_type;
	
		switch (v.m_type.GetTypeID())
		{
			case Type::STRING:
				m_value.v_string = new std::string(*v.m_value.v_string);
				break;
			default:
				m_value = v.m_value;
		}
	}

	return *this;
}

bool Value::operator==(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::BOOL:
			switch (v.m_type.GetTypeID())
			{
				case Type::BOOL:
					return m_value.v_boolean == v.m_value.v_boolean;
				case Type::STRING:
					return ToString() == *v.m_value.v_string;
				default:;
			}
			break;
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return m_value.v_int == v.m_value.v_int;
				case Type::FLOAT:
					return m_value.v_int == v.m_value.v_float;
				case Type::STRING:
					return ToString() == *v.m_value.v_string;
				default:;
			}
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return m_value.v_float == v.m_value.v_int;
				case Type::FLOAT:
					return m_value.v_float == v.m_value.v_float;
				case Type::STRING:
					return ToString() == *v.m_value.v_string;
				default:;
			}
			break;
		case Type::STRING:
			return *m_value.v_string == v.ToString();
		default:;
	}

	assert(false);
	return false;
}

bool Value::operator<(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return m_value.v_int < v.m_value.v_int;
				case Type::FLOAT:
					return static_cast<float>(m_value.v_int) < v.m_value.v_float;
				default:;
			}
			break;
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return m_value.v_float < static_cast<float>(v.m_value.v_int);
				case Type::FLOAT:
					return m_value.v_float < v.m_value.v_float;
				default:;
			}
			break;
		case Type::STRING:
			switch (v.m_type.GetTypeID())
			{
				case Type::STRING:
					return std::strcmp(m_value.v_string->c_str(), v.m_value.v_string->c_str()) < 0;
				default:;
			}
			break;
		default:;
	}
	
	assert(false);
	return false;
}

const Value Value::operator+(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_int + v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_int + v.m_value.v_float);
				case Type::STRING:
					return Value(ToString() + *v.m_value.v_string);
				default:;
			}
			break;
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_float + v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_float + v.m_value.v_float);
				case Type::STRING:
					return Value(ToString() + *v.m_value.v_string);
				default:;
			}
			break;
		case Type::STRING:
			switch (v.m_type.GetTypeID())
			{
				case Type::STRING:
					return Value(*m_value.v_string + *v.m_value.v_string);
				
				case Type::INT:
				case Type::FLOAT:
					return Value(*m_value.v_string + v.ToString());
				default:;
			}
			break;
		default:;
	}

	assert(false);
	return Value(Type::NONE);
}

const Value Value::operator-(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_int - v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_int - v.m_value.v_float);
				default:;
			}
			break;
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_float - v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_float - v.m_value.v_float);
				default:;
			}
			break;
		default:;
	}

	assert(false);
	return Value(Type::NONE);
}

inline std::string duplicate(const std::string &str, int n)
{
	std::string result;
	for (int i = 0; i < n; i++)
		result += str;
	return result;
}

const Value Value::operator*(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_int * v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_int * v.m_value.v_float);
				case Type::STRING:
					return Value(duplicate(*v.m_value.v_string, m_value.v_int));
				default:;
			}
			break;
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_float * v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_float * v.m_value.v_float);
				default:;
			}
			break;
		case Type::STRING:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(duplicate(*m_value.v_string, v.m_value.v_int));
				default:;
			}
			break;
		default:;
	}

	assert(false);
	return Value(Type::NONE);
}

const Value Value::operator/(const Value &v) const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_int / v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_int / v.m_value.v_float);
				default:;
			}
			break;		
		case Type::FLOAT:
			switch (v.m_type.GetTypeID())
			{
				case Type::INT:
					return Value(m_value.v_float / v.m_value.v_int);
				case Type::FLOAT:
					return Value(m_value.v_float / v.m_value.v_float);
				default:;
			}
			break;
		default:;
	}

	assert(false);
	return Value(Type::NONE);
}

const Value Value::operator-() const
{
	switch (m_type.GetTypeID())
	{
		case Type::INT: return Value(-m_value.v_int);
		case Type::FLOAT: return Value(-m_value.v_float);
		default:;
	}

	assert(false);
	return Value(Type::NONE);
}


bool Value::ToBoolean() const
{
	switch (m_type.GetTypeID())
	{
		case Type::BOOL:		return m_value.v_boolean;
		case Type::INT:			return m_value.v_int != 0;
		case Type::FLOAT:		return m_value.v_float != 0.0f;
		case Type::STRING:		return m_value.v_string->size() != 0;
		default:;
	}
	
	assert(false);
	return false;
}

int Value::ToInt() const
{
	switch (m_type.GetTypeID())
	{
		case Type::BOOL:			return m_value.v_boolean ? 1 : 0;
		case Type::INT:				return m_value.v_int;
		case Type::FLOAT:			return m_value.v_float;
		case Type::STRING:			return atoi(m_value.v_string->c_str());
		default:;
	}

	assert(false);
	return 0.0f;;
}

float Value::ToFloat() const
{
	switch (m_type.GetTypeID())
	{
		case Type::BOOL:			return m_value.v_boolean ? 1.0f : 0.0f;
		case Type::INT:				return m_value.v_int;
		case Type::FLOAT:			return m_value.v_float;
		case Type::STRING:			return static_cast<float>(atof(m_value.v_string->c_str()));
		default:;
	}

	assert(false);
	return 0.0f;
}

std::string Value::ToString() const
{
	char buffer[32];

	switch (m_type.GetTypeID())
	{
		case Type::BOOL:			return std::string( m_value.v_boolean ? "true" : "false" );
		case Type::INT:				sprintf(buffer, "%d", m_value.v_int); return std::string(buffer);
		case Type::FLOAT:			sprintf(buffer, "%g", m_value.v_float); return std::string(buffer);
		case Type::STRING:			return *m_value.v_string;
		default:;
	}

	assert(false);
	return std::string("{UNSUPPORTED TYPE}");
}

int Value::BaseToDec(const char* ch, int base)
{
	int result = 0;

	for ( ; *ch; ch++)
	{
		if (*ch >= '0' && *ch <= '9' && *ch <= '0'+base)
			result = base*result + *ch-'0';
		else if (*ch >= 'A' && *ch <= 'A'+base-11)
			result = base*result + *ch-'A'+10;
		else if (*ch >= 'a' && *ch <= 'a'+base-11)
			result = base*result + *ch-'a'+10;
	}

	return result;
}
